package logger

import (
	"fmt"
	"os"
	"path/filepath"
	"runtime"
)

// Record
type Record struct {
	Level       LogLevel `json:"-"`
	LevelString string   `json:"level"`
	LogFile     string   `json:"log_file"`
	Message     string   `json:"message"`
	KvField     []Field  `json:"-"`
	KvJson      string   `json:"-"`
	Pid         int      `json:"-"`
	Program     string   `json:"program"`
	Time        string   `json:"time"`
	FuncName    string   `json:"func_name"`
	Path        string   `json:"path"`
	FileName    string   `json:"file"`
	Line        int      `json:"line"`
	Color       string   `json:"-"`
	ColorClear  string   `json:"-"`
}

// GetMessageRecord, make a record and return it's reference.
func GetMessageRecord(level LogLevel, format string, a ...interface{}) *Record {
	message := fmt.Sprintf(format, a...)
	pc, file, line, _ := runtime.Caller(4)
	record := &Record{
		Level:       level,
		Message:     message,
		Pid:         os.Getpid(),
		Program:     filepath.Base(os.Args[0]),
		Time:        "",
		FuncName:    runtime.FuncForPC(pc).Name(),
		Path:        file,
		FileName:    filepath.Base(file),
		Line:        line,
		Color:       LevelColorFlag[level],
		ColorClear:  LevelColorSetClear,
		LevelString: LevelString[level],
	}
	return record
}

// GetMessageKVRecord, make a record and return it's reference.
func GetMessageKVRecord(level LogLevel, message string, kv ...interface{}) *Record {
	pc, file, line, _ := runtime.Caller(4)
	fields := sweetenFields(kv)
	record := &Record{
		Level:       level,
		Message:     message,
		KvField:     fields,
		Pid:         os.Getpid(),
		Program:     filepath.Base(os.Args[0]),
		FuncName:    runtime.FuncForPC(pc).Name(),
		Path:        file,
		FileName:    filepath.Base(file),
		Line:        line,
		Color:       LevelColorFlag[level],
		ColorClear:  LevelColorSetClear,
		LevelString: LevelString[level],
	}

	return record
}

type invalidPair struct {
	position   int
	key, value interface{}
}
type invalidPairs []invalidPair

func sweetenFields(args []interface{}) []Field {
	if len(args) == 0 {
		return nil
	}

	// Allocate enough space for the worst case; if users pass only structured
	// fields, we shouldn't penalize them with extra allocations.
	fields := make([]Field, 0, len(args))
	var invalid invalidPairs

	for i := 0; i < len(args); i += 2 {

		// Make sure this element isn't a dangling key.
		if i == len(args)-1 {
			panic(_oddNumberErrMsg)
			break
		}

		// Consume this value and the next, treating them as a key-value pair. If the
		// key isn't a string, add this pair to the slice of invalid pairs.
		key, val := args[i], args[i+1]
		if keyStr, ok := key.(string); !ok {
			// Subsequent errors are likely, so allocate once up front.
			if cap(invalid) == 0 {
				invalid = make(invalidPairs, 0, len(args)/2)
			}
			invalid = append(invalid, invalidPair{i, key, val})
		} else {
			fields = append(fields, Any(keyStr, val))
		}
	}

	// If we encountered any invalid key-value pairs, log an error.
	if len(invalid) > 0 {
		panic(_nonStringKeyErrMsg)
	}
	return fields
}
